#!/usr/bin/python
import os
import sys
import json
import argparse
import subprocess
import re

## 
## a. look at the make command
## b. run the command with CC and CXX as own executable
## c. pipe the commands back to parent when the child executes
## d. when the make returns write to file and quit
##
## execution examples:
## cis_make.py  --cis-makecommand make --cis-compiledb /tmp/compdb.json --cis-makeparams '-C ~/Downloads/poco-1.4.4'
## cis_make.py --cis-docompile --cis-makecommand make --cis-compiledb /tmp/compdb.json --cis-makeparams '-C ~/Downloads/poco-1.4.4'
lang_map = {
 '.*\.c$'   : 'CC',
 '.*\.C$'   : 'CC',
 '.*\.cp$'  : 'C++',
 '.*\.cpp$' : 'C++',
 '.*\.cxx$' : 'C++',
 '.*\.txx$' : 'C++',
 '.*\.cc$'  : 'C++',
 '.*.ii$'  : 'C++',
}

opt_regexp = "-.*"

def getFileNameAndType(command):
    commandList = command.split(" ")
    try:
        commandList.index("-c")
    except:
        return None
     
    for param in commandList:
        for item in lang_map.keys():
            #print "matching ", param, " - ", item
            if (not re.match(opt_regexp, param)):
                for regexp in lang_map.keys():
                    #print "Testing ", regexp
                    if (re.match(regexp, param)):
                        return [param, lang_map[regexp]]
            else:
                break
    return None
    
def removeFileIfExists(filename):
    try:
        os.remove(filename)
    except:
        pass

def doInSubProcess(command):    
    proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while True:
        char = proc.stdout.read(1)
        if char != '':
            sys.stdout.write(char)
        else:
            break;
    proc.wait()
    return proc.returncode

def getCleanCommand(makecommand, makeparams):
    if (len(makeparams) > 0):
        makeparams = makeparams.split(" ")
        cleancommand = [makecommand, 'clean']
        cleancommand.extend(makeparams)
    else:
        cleancommand = [makecommand, 'clean']
    print cleancommand
    return cleancommand
    
def getBuildCommand(makecommand, makeparams, docompile, compiledb):
    appendstr = ""
    ccstr = " --cis-compiler clang"
    cppstr = " --cis-compiler clang++"
    if (len(makeparams) > 0):
        makeparams = makeparams.split(" ")
    
    if (docompile):
        appendstr = "--cis-docompile --cis-compiledb " + compiledb
        if (len(makeparams) == 0):
            makecommand = [makecommand, "CC=" + os.path.realpath(__file__) + " " + appendstr + ccstr, "CXX=" + os.path.realpath(__file__) + " " + appendstr + cppstr]
        else:
            makecommand = [makecommand, "CC=" + os.path.realpath(__file__) + " " + appendstr + ccstr, "CXX=" + os.path.realpath(__file__) + " " + appendstr + cppstr]
            makecommand.extend(makeparams)
    else:
        appendstr = "--cis-compiledb " + compiledb
        if (len(makeparams) == 0):
            makecommand = [makecommand, " CC=" + os.path.realpath(__file__) + " " + appendstr + ccstr, " CXX=" + os.path.realpath(__file__) + " " + appendstr + cppstr]
        else:
            makecommand = [makecommand, " CC=" + os.path.realpath(__file__) + " " + appendstr + ccstr, " CXX=" + os.path.realpath(__file__) + " " + appendstr + cppstr]
            makecommand.extend(makeparams)
    
    return makecommand
        
def child(compiler, docompile, compiledb):
    if (docompile):
        del sys.argv[0]
    del sys.argv[0]
    del sys.argv[0]
    del sys.argv[0]
    del sys.argv[0]
    del sys.argv[0]
    
    cwd = os.getcwd()
    command = " ".join(sys.argv)
    compdbtmp = compiledb
    datafile = open(compdbtmp, "a+")
    nameAndType = getFileNameAndType(command)

    if (nameAndType is not None):
        if os.path.getsize(compdbtmp) <= 0:
            json.dump({'directory' : cwd, 'command' : command, 'filename' : nameAndType[0]}, datafile)
        else:
            datafile.write(",")
            json.dump({'directory' : cwd, 'command' : command, 'filename' : nameAndType[0]}, datafile)
    if (docompile):
        compilecmd = [compiler] + sys.argv
        doInSubProcess(compilecmd)
    datafile.close()
    
def writeCompilationDB(compiledbtmp, compiledb):
    print "scanning finished, writing compilation db from file "
    try:
        data = open(compiledbtmp, "r").read()
    except:
        print "no compilation database produced! please check the errors in make."
        exit()
    compiledbfile = open(compiledb, "w+")
    compiledbfile.write("[")
    compiledbfile.write(data)
    compiledbfile.write("]")
    compiledbfile.close()
    
def parent(makecommand, compiledb, docompile, makeparams):
    compiledbtmp = compiledb + ".tmp"
    buildcommand = getBuildCommand(makecommand, makeparams, docompile, compiledbtmp)
    cleancommand = getCleanCommand(makecommand, makeparams)
    removeFileIfExists(compiledbtmp)
    removeFileIfExists(compiledb)
    print "clean command is ", cleancommand
    doInSubProcess(cleancommand)
    doInSubProcess(buildcommand)
    writeCompilationDB(compiledbtmp, compiledb)
    
makecommand = None
compiledb = os.path.dirname(os.path.realpath(__file__)) + os.sep + "compilationdb.json"

parser = argparse.ArgumentParser(description='cis build scanner')

parser.add_argument('--cis-makecommand', dest='makecommand', action="store",
                    help='make command to execute', required=False)
                    
parser.add_argument('--cis-makeparams', dest='makeparams', default = "", action="store",
                    help='make command parameters', required=False)
                    
parser.add_argument('--cis-compiledb', dest='compiledb', action="store",
                    help='compilation db filename', default=compiledb, 
                    required=True)
                    
parser.add_argument('--cis-docompile', dest='docompile', action="store_true",
                    help='compilation db filename', default=False, 
                    required=False)
                    
parser.add_argument('--cis-compiler', dest='compiler', action="store",
                    help='compiler to use',  
                    required=False)
                    
args, extra = parser.parse_known_args()
print args.makeparams
if (args.makecommand == None):
    #child invocation
    child(args.compiler, args.docompile, args.compiledb)
    pass
else:
    #parent invocation
    parent(args.makecommand, args.compiledb, args.docompile, args.makeparams)    